/*
日志器模块
1.抽象出日志器基类
2.对基类进行派生出两个不同落地方式的子类（同步日志器&&异步日志器
*/

#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__

#include "util.hpp"
#include "message.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "looper.hpp"
#include <mutex>
#include <atomic>
#include <cstdarg>
#include <cassert>
#include <unordered_map>

namespace clog
{
    enum class LoggerType
    {
        LOG_SYNC = 0,
        LOG_ASYNC
    };
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string &logger_name,
               LogLevel::value level,
               Formatter::ptr formatter,
               std::vector<LogSink::ptr> &sinks)
            : _logger_name(logger_name), _level(level), _formatter(formatter), _sinks(sinks.begin(), sinks.end())
        {
        }
        std::string GetLoggerName()
        {
            return _logger_name;
        }
        // 构造出日志消息对象，并进行格式化，格式化成消息字符串后再落地输出
        void debug(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造出一个日志消息对象，并格式化后落地输出
            // 1.判断当前输出等级是否达到限制的输出等级
            if (LogLevel::value::DEBUG < _level)
            {
                return;
            }
            // 构造出一个日志消息对象
            va_list ap;
            va_start(ap, fmt);
            char *str;
            int ret = vasprintf(&str, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf faild!\n";
                return;
            }
            va_end(ap);
            serialize(LogLevel::value::DEBUG, file, line, str);
            free(str); // 避免内存泄露
        }
        void info(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造出一个日志消息对象，并格式化后落地输出
            // 1.判断当前输出等级是否达到限制的输出等级
            if (LogLevel::value::INFO < _level)
            {
                return;
            }
            // 构造出一个日志消息对象
            va_list ap;
            va_start(ap, fmt);
            char *str;
            int ret = vasprintf(&str, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf faild!\n";
                return;
            }
            va_end(ap);
            serialize(LogLevel::value::INFO, file, line, str);
            free(str); // 避免内存泄露
        }
        void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造出一个日志消息对象，并格式化后落地输出
            // 1.判断当前输出等级是否达到限制的输出等级
            if (LogLevel::value::WARM < _level)
            {
                return;
            }
            // 构造出一个日志消息对象
            va_list ap;
            va_start(ap, fmt);
            char *str;
            int ret = vasprintf(&str, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf faild!\n";
                return;
            }
            va_end(ap);
            serialize(LogLevel::value::WARM, file, line, str);
            free(str); // 避免内存泄露
        }
        void error(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造出一个日志消息对象，并格式化后落地输出
            // 1.判断当前输出等级是否达到限制的输出等级
            if (LogLevel::value::ERROR < _level)
            {
                return;
            }
            // 构造出一个日志消息对象
            va_list ap;
            va_start(ap, fmt);
            char *str;
            int ret = vasprintf(&str, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf faild!\n";
                return;
            }
            va_end(ap);
            serialize(LogLevel::value::ERROR, file, line, str);
            free(str); // 避免内存泄露
        }
        void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造出一个日志消息对象，并格式化后落地输出
            // 1.判断当前输出等级是否达到限制的输出等级
            if (LogLevel::value::FATAL < _level)
            {
                return;
            }
            // 构造出一个日志消息对象
            va_list ap;
            va_start(ap, fmt);
            char *str;
            int ret = vasprintf(&str, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf faild!\n";
                return;
            }
            va_end(ap);
            serialize(LogLevel::value::FATAL, file, line, str);
            free(str); // 避免内存泄露
        }

    protected:
        void serialize(LogLevel::value level, const std::string &file, size_t line, const std::string &str)
        {
            LogMsg msg(level, line, file, _logger_name, str);
            // 格式化
            std::stringstream ss;
            _formatter->format(ss, msg);
            // 落地输出
            log(ss.str().c_str(), ss.str().size());
        }

        // 根据不同的日志器类型 派生出不同落地方式的子类
        virtual void log(const char *data, size_t len) = 0;
        std::mutex _mutex;
        std::string _logger_name;
        std::atomic<LogLevel::value> _level;
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    // 同步日志器  将格式化后的日志消息直接通过日志落地模块句柄进行落地
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &logger_name,
                   LogLevel::value level,
                   Formatter::ptr formatter,
                   std::vector<LogSink::ptr> &sinks)
            : Logger(logger_name, level, formatter, sinks)
        {
        }

    protected:
        void log(const char *data, size_t len) override
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                sink->log(data, len);
            }
        }
    };

    // 异步日志器--业务线程将数据写入缓冲区后，异步工作线程从缓冲区中拿取数据写入磁盘（双缓冲区--减少锁冲突--主要是生产者和消费者之间的冲突）
    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &logger_name,
                    LogLevel::value level,
                    Formatter::ptr formatter,
                    std::vector<LogSink::ptr> &sinks, AsyncType looper_type)
            : Logger(logger_name, level, formatter, sinks), _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::reallog, this, std::placeholders::_1), looper_type))
        {
        }

    protected:
        // 首先落地到输入缓冲区
        void log(const char *data, size_t len) override
        {
            _looper->push(data, len);
        }
        // 实际的落地方向--对从处理缓冲区中拿到的数据进行处理
        void reallog(Buffer &buffer)
        {
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                sink->log(buffer.begin(), buffer.readAbleSize());
            }
        }

    private:
        AsyncLooper::ptr _looper;
    };

    // 建造者模式
    /*
    1.抽象出一个日志器建造者基类
      1.设置日志器类型（同步&&异步）
      2.实现各个接口，完成一个日志器各个零部件的构建
      3.将不同类型的日志器放在同一个建造者类中实现
    */

    class LoggerBuilder
    {
    public:
        LoggerBuilder()
            : _logger_type(LoggerType::LOG_SYNC), _level(LogLevel::value::DEBUG), _looper_type(AsyncType::ASYNC_SAVE)
        {
        }
        void buildEnableUnsaveAsync()
        {
            _looper_type = AsyncType::ASYNC_UNSAVE;
        }
        void buildloggertype(LoggerType type)
        {
            _logger_type = type;
        }
        void buildloggername(const std::string &name)
        {
            _logger_name = name;
        }
        void buildloggerlevel(LogLevel::value level)
        {
            _level = level;
        }
        void buildformatter(const std::string &pattern)
        {
            _formatter = std::make_shared<Formatter>(pattern);
        }
        template <typename sinktype, typename... Args>
        void buildsink(Args &&...args)
        {
            LogSink::ptr psink = std::make_shared<sinktype>(std::forward<Args>(args)...);
            _sinks.push_back(psink);
        }
        virtual Logger::ptr build() = 0;

    protected:
        LoggerType _logger_type;
        std::string _logger_name;
        LogLevel::value _level;
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
        AsyncType _looper_type;
    };
    /*
    2.派生出具体的建造者类--局部日志器建造者&&全局日志器建造者（添加全局单例管理器之后，将日志器添加到全局管理)
    */
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(!_logger_name.empty()); // 必须要有日志器名称
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            if (_sinks.empty())
            {
                buildsink<StdoutSink>();
            }
            if (_logger_type == LoggerType::LOG_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_logger_name, _level, _formatter, _sinks, _looper_type);
            }
            return std::make_shared<SyncLogger>(_logger_name, _level, _formatter, _sinks);
        }
    };

    // 日志器管理器--单例对象
    class LoggerManager
    {
    private:
        LoggerManager()
        {
            std::unique_ptr<LoggerBuilder> lb(new LocalLoggerBuilder()); // 不能使用全局建造者 会陷入循环阻塞
            lb->buildloggername("root");                                 // 默认日志器只需要构建一个日志器名称
            lb->buildloggertype(LoggerType::LOG_SYNC);                   // 同步日志器
            _root_logger = lb->build();
            _loggers.insert(std::make_pair("root", _root_logger));
        }

    public:
        LoggerManager(const LoggerManager &lm) = delete;
        const LoggerManager &operator=(const LoggerManager &lm) = delete;
        static LoggerManager &GetInstance()
        {
            static LoggerManager lm;
            return lm;
        }
        Logger::ptr getLogger(const std::string &name)
        {
            // 加锁
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return Logger::ptr();
            }
            return it->second;
        }
        bool hashLogger(const std::string &name)
        {
            // 加锁
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return false;
            }

            return true;
        }
        void addLogger(const std::string &name, const Logger::ptr logger)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(name, logger));
        }
        Logger::ptr getRootLogger()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _root_logger;
        }

    private:
        std::mutex _mutex;
        // 默认日志器
        Logger::ptr _root_logger;
        // 用于存储日志器和日志器名称映射关系的哈希表
        std::unordered_map<std::string, Logger::ptr> _loggers;
    };

    // 全局日志器建造者--将日志器添加到单例对象中去
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        using ptr = std::shared_ptr<GlobalLoggerBuilder>;
        Logger::ptr build() override
        {
            assert(!_logger_name.empty()); // 必须要有日志器名称
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            if (_sinks.empty())
            {
                buildsink<StdoutSink>();
            }
            Logger::ptr logger;
            if (_logger_type == LoggerType::LOG_ASYNC)
            {
                logger = std::make_shared<AsyncLogger>(_logger_name, _level, _formatter, _sinks, _looper_type);
            }
            else
            {
                logger = std::make_shared<SyncLogger>(_logger_name, _level, _formatter, _sinks);
            }
            // 添加到单例对象中
            LoggerManager::GetInstance().addLogger(_logger_name, logger);
            return logger;
        }
    };
}
#endif